#include <jni.h>
#include <string>
#include "LogUtils.h"
#include "GLRender.h"

#define NATIVE_INTERFACE_CLASS_NAME "com/wmd102/opengldemo/NativeInterface"

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT void JNICALL native_OnInit(JNIEnv *env, jobject instance) {
    LOG_DEBUG("native_OnInit S\n");
    GLRender::GetInstance();
}

JNIEXPORT void JNICALL native_OnUnInit(JNIEnv *env, jobject instance) {
    LOG_DEBUG("native_OnUnInit S\n");
    GLRender::DestroyInstance();
}

JNIEXPORT void JNICALL native_SetImageData(
        JNIEnv *env, jobject obj,
        jint format, jint w, jint h, jbyteArray data
) {
    LOG_DEBUG("native_SetImageData S\n");
    int len = env->GetArrayLength(data);
    uint8_t *buf = new uint8_t[len];
    env->GetByteArrayRegion(
            data, 0, len,
            reinterpret_cast<jbyte *>(buf));
    GLRender::GetInstance()->SetImageData(
            format, w, h, buf);
    delete[] buf;
    env->DeleteLocalRef(data);
}
JNIEXPORT void JNICALL native_SetParamsInt(
        JNIEnv *env, jobject obj, jint paramType, jint value
) {
    LOG_DEBUG("native_SetParamsInt S\n");
    GLRender::GetInstance()->SetParamsInt(paramType, value);
}
JNIEXPORT void JNICALL native_OnSurfaceCreated(JNIEnv *env, jobject instance) {
    LOG_DEBUG("native_OnSurfaceCreated S\n");
    GLRender::GetInstance()->OnSurfaceCreated();
}

JNIEXPORT void JNICALL native_OnSurfaceChanged
        (JNIEnv *env, jobject instance, jint width, jint height) {
    LOG_DEBUG("native_OnSurfaceChanged S\n");
    GLRender::GetInstance()->OnSurfaceChanged(width, height);
}

JNIEXPORT void JNICALL native_OnDrawFrame(JNIEnv *env, jobject instance) {
    LOG_DEBUG("native_OnDrawFrame S\n");
    GLRender::GetInstance()->OnDrawFrame();
}

#ifdef __cplusplus
}
#endif

static JNINativeMethod g_RenderMethods[] = {
        {"native_OnInit",           "()V",      (void *) (native_OnInit)},
        {"native_OnUnInit",         "()V",      (void *) (native_OnUnInit)},
        {"native_SetImageData",     "(III[B)V", (void *) (native_SetImageData)},
        {"native_SetParamsInt",     "(II)V",    (void *) (native_SetParamsInt)},
        {"native_OnSurfaceCreated", "()V",      (void *) (native_OnSurfaceCreated)},
        {"native_OnSurfaceChanged", "(II)V",    (void *) (native_OnSurfaceChanged)},
        {"native_OnDrawFrame",      "()V",      (void *) (native_OnDrawFrame)},
};

extern "C" jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    LOG_DEBUG("native JNI_OnLoad S\n");

    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }

    jclass nativeInterfaceClass = env->FindClass(NATIVE_INTERFACE_CLASS_NAME);
    if (nativeInterfaceClass == NULL) {
        return JNI_ERR;
    }

    if (env->RegisterNatives(nativeInterfaceClass, g_RenderMethods,
                             sizeof(g_RenderMethods) /
                             sizeof(g_RenderMethods[0])) < 0) {
        return JNI_ERR;
    }

    LOG_DEBUG("native JNI_OnLoad OK\n");

    return JNI_VERSION_1_6;
}

extern "C" void JNI_OnUnload(JavaVM *vm, void *reserved) {
    LOG_DEBUG("native JNI_OnUnload S\n");

    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return;
    }

    jclass nativeInterfaceClass = env->FindClass(NATIVE_INTERFACE_CLASS_NAME);
    if (nativeInterfaceClass == NULL) {
        return;
    }

    env->UnregisterNatives(nativeInterfaceClass);

    LOG_DEBUG("native JNI_OnUnload OK\n");
}

/**
 * QA：JAVA方法带_，根据错误提示，添加“1”
 */
extern "C" JNIEXPORT jstring JNICALL
Java_com_wmd102_opengldemo_NativeInterface_native_1GetLibVersion(
        JNIEnv *env,
        jobject /* this */) {
    std::string version = "V1.0.0.20210114";
    LOG_DEBUG("native_GetLibVersion %s\n", version.c_str());
    return env->NewStringUTF(version.c_str());
}
